﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
using winS.Unity.Process;
using winS.UnityEditor.UIElement;
using static winS.UnityEditor.ProcessEditor.ProcessEditorData;

namespace winS.UnityEditor.ProcessEditor
{
    public class StepGraphView : VisualElement
    {
        public float minScale => zoomer.minScale;
        public float maxScale => zoomer.maxScale;
        public float currentScale => zoomer.currentScale;

        /// <summary>
        /// 所有视图中的节点
        /// </summary>
        public IEnumerable<StepNode> stepNodes => stepNodeList;
        public IEnumerable<GraphElement> graphElements => graphElementList;
        public ITransform viewTransform => contentViewContainer.transform;
        public ProcessEditor processEditor { get; private set; }
        public VisualElement contentViewContainer { get; }

        internal bool canCallOnReady => stepNodeCreateState != StepNodeCreateState.CreatingFromFile;
        internal bool canRegisterUndo => stepNodeCreateState == StepNodeCreateState.None;
        /// <summary>
        /// 是否可以注册删除相关的撤销事件
        /// </summary>
        internal bool canRegisterDeleteUndo => stepNodeCreateState is StepNodeCreateState.Deleting or StepNodeCreateState.Cut;

        /// <summary>
        /// 是否允许端口发送连接/断开连接事件
        /// </summary>
        internal bool canSendConnectEvent => stepNodeCreateState is StepNodeCreateState.None or StepNodeCreateState.CreateAndConnect or StepNodeCreateState.Cut or StepNodeCreateState.Deleting;
        internal StepNodeCreateState stepNodeCreateState { get; set; }
        internal OutputPort waitConnectionOutputPort { get; set; }

        internal readonly VisualElement nodeContainer;
        internal readonly VisualElement connectionlineContainer;
        internal readonly List<StepNode> stepNodeList = new List<StepNode>();
        internal readonly List<Connectionline> connectionlineList = new List<Connectionline>();
        internal readonly HashSet<GraphElement> selectionHashSet = new HashSet<GraphElement>();

        internal readonly Mover mover;
        internal readonly Zoomer zoomer;
        internal readonly Dragger dragger;
        internal readonly Selector selector;
        internal readonly ZoomControl zoomControl;

        private IProcess currentProcess => currentEditorProcessInfo?.process;

        private string titleFilter;
        private EditorProcessInfo currentEditorProcessInfo;

        private readonly FileViewData fileViewData;
        /// <summary>
        /// 节点搜索视图
        /// </summary>
        private readonly StepNodeSearchWindow stepNodeSearchWindow;

        private readonly List<string> copyBufferList = new List<string>();
        private readonly List<GraphElement> graphElementList = new List<GraphElement>();
        private readonly Dictionary<IStep, StepNode> stepDictionary = new Dictionary<IStep, StepNode>(7);
        private readonly Dictionary<string, StepNode> stepNodeDictionary = new Dictionary<string, StepNode>(7);
        private readonly Dictionary<Type, Type> stepNodeTypeDictionary = new Dictionary<Type, Type>(7);

        public StepGraphView(ProcessEditor processEditor)
        {
            focusable = true;
            style.flexGrow = 1f;
            style.overflow = Overflow.Hidden;
            style.alignItems = Align.FlexStart;

            fileViewData = processEditor.editorData.fileViewData;
            RegisterEditorSettingChanged(processEditor.editorData);
            Add(new GridBackground(processEditor.editorData.backgroundData));

            //添加容器
            Add(contentViewContainer = EditorElementUtility.elementFactory.CreateEditorElementWithName("流程图内容"));
            contentViewContainer.usageHints = UsageHints.GroupTransform;//标记为GroupTransform, 防止在修改位置和缩放时触发子元素(如连接线)的重绘
            contentViewContainer.pickingMode = PickingMode.Ignore;

            contentViewContainer.Add(connectionlineContainer = EditorElementUtility.elementFactory.CreateEditorElementWithName("连接线容器"));
            connectionlineContainer.style.position = Position.Absolute;

            contentViewContainer.Add(nodeContainer = EditorElementUtility.elementFactory.CreateEditorElementWithName("节点容器"));
            nodeContainer.style.position = Position.Absolute;

            this.processEditor = processEditor;

            stepNodeSearchWindow = ScriptableObject.CreateInstance<StepNodeSearchWindow>();
            stepNodeSearchWindow.Initialize(this, processEditor);

            //添加缩放提示器
            Add(zoomControl = new ZoomControl());

            RegisterCallback<KeyDownEvent>(KeyDownCallback);
            RegisterCallback<MouseDownEvent>(MouseDownCallback);
            RegisterCallback<ValidateCommandEvent>(ValidateCommandCallback);
            RegisterCallback<ContextualMenuPopulateEvent>(ContextualMenuPopulateCallback);

            //处理内置节点
            stepNodeTypeDictionary.Add(typeof(StartStep), typeof(StartStepNode<StartStep>));
            AddBuiltinNode<RouletteRandomStep, RouletteRandomStepNode>();
            AddBuiltinNode<ConnectionStep, ConnectionStepNode>();
            AddBuiltinNode<AllPassStep, AllPassStepNode>();
            AddBuiltinNode<RandomStep, RandomStepNode>();
            AddBuiltinNode<DebugStep, DebugStepNode>();
            AddBuiltinNode<WaitStep, WaitStepNode>();

            //处理扩展节点
            ReflectCustomStepNode();
            //生成搜索树，输入后现实有多少匹配名称的插件
            stepNodeSearchWindow.BuildSearchTree();

            mover = new Mover(this);
            zoomer = new Zoomer(this);
            dragger = new Dragger(this);
            selector = new Selector(this, processEditor.editorData);
            dragger.onDragEnd += mover.DisableEdgePan;
            dragger.onDragStart += mover.EnableEdgePan;
            selector.onBoxSelectEnd += mover.DisableEdgePan;
            selector.onBoxSelectStart += mover.EnableEdgePan;
            zoomer.onScaleChanged += zoomControl.SetZoom;
            mover.onEdgePan += selector.PanRectangle;
            mover.onEdgePan += dragger.PanElement;

            zoomControl.SetZoom(zoomer.ratio);
            void AddBuiltinNode<T, K>()
            {
                Type stepType = typeof(T);
                Type stepNodeType = typeof(K);
                stepNodeTypeDictionary.Add(stepType, stepNodeType);
                stepNodeSearchWindow.AddCommonEntry(stepNodeType.GetCustomAttribute<StepNodeSearchTreeGroupAttribute>().groupName, stepType);
            }
        }

        public void Save()
        {
            EditorData editorData = currentProcess.editorData;
            if (editorData.viewDataSaveTargetType == EditorData.DataSaveTargetType.SaveToFile)
            {
                editorData.viewData.offset = contentViewContainer.transform.position;
                editorData.viewData.scale = contentViewContainer.transform.scale.x;
            }
        }
        public void ClearProcess()
        {
            if (currentEditorProcessInfo == null) return;
            foreach (var stepNode in stepNodeList) stepNode.OnDestroy();
            stepNodeDictionary.Clear();
            nodeContainer.Clear();
            stepNodeList.Clear();

            connectionlineContainer.Clear();
            connectionlineList.Clear();

            selectionHashSet.Clear();
            graphElementList.Clear();
        }
        public void ClearSelection()
        {
            foreach (var graphElement in selectionHashSet) graphElement.Unselect();
            selectionHashSet.Clear();
        }
        public void AddToSelection(GraphElement graphElement)
        {
            if (!selectionHashSet.Add(graphElement)) throw new Exception($"{nameof(StepGraphView)}.{nameof(RemoveFromSelection)}: 参数 {nameof(graphElement)} 已在选择队列中");
            graphElement.Select();
            graphElement.BringToFront();
        }
        public void RemoveFromSelection(GraphElement graphElement)
        {
            if (!selectionHashSet.Remove(graphElement)) throw new Exception($"{nameof(StepGraphView)}.{nameof(RemoveFromSelection)}: 参数 {nameof(graphElement)} 没有在选择队列中");
            graphElement.Unselect();
        }
        public void AddConnectionline(Connectionline connectionline)
        {
            connectionlineList.Add(connectionline);
            connectionlineContainer.Add(connectionline);
            graphElementList.Add(connectionline);
        }
        public void RemoveConnectionline(Connectionline connectionline)
        {
            connectionlineList.Remove(connectionline);
            connectionlineContainer.Remove(connectionline);
            graphElementList.Remove(connectionline);
        }

        public void SetZoom(float minScale, float maxScale, float currentScale, float zoomInterval)
        {
            zoomer.SetZoom(minScale, maxScale, currentScale, zoomInterval);
        }
        public void NodeFilter(string titleFilter)
        {
            this.titleFilter = titleFilter;
            if (string.IsNullOrEmpty(titleFilter))
            {
                for (int i = 0; i < stepNodeList.Count; i++)
                {
                    StepNode stepNode = stepNodeList[i];
                    stepNode.style.opacity = 1f;
                    stepNode.SetEnabled(true);
                }
                return;
            }
            for (int i = 0; i < stepNodeList.Count; i++)
            {
                StepNode stepNode = stepNodeList[i];
                bool isContains = stepNode.title.Contains(titleFilter);
                stepNode.style.opacity = isContains ? 1f : 0.2f;
                stepNode.SetEnabled(isContains);
            }
        }
        public IStep CreateStep(Type stepType, Vector2 position)
        {
            IStep step = (IStep)Activator.CreateInstance(stepType);
            step.guid = EditorProcessAssetManager.NewGUID();
            step.drawingData.position = position;
            return step;
        }
        /// <summary>
        /// 选单中创建节点
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public StepNode CreateStepNode(IStep step)
        {
            if (stepDictionary.TryGetValue(step, out StepNode stepNode)) return stepNode;
            if (!stepNodeTypeDictionary.TryGetValue(step.GetType(), out Type editorType)) throw new Exception($"{nameof(StepGraphView)}.{nameof(CreateStepNode)}: 步骤 {step.GetType().Name} 没有对应的编辑类型");
            stepNode = Activator.CreateInstance(editorType) as StepNode;
            stepNodeDictionary.Add(step.guid, stepNode);
            stepNode.guiFactory = processEditor.guiFactory;
            stepNode.process = currentProcess;
            stepNode.stepGraphView = this;
            stepNode.step = step;
            stepNode.Initialize();
            stepNode.OnCreated();

            stepNodeList.Add(stepNode);
            nodeContainer.Add(stepNode);
            graphElementList.Add(stepNode);

            stepDictionary.Add(step, stepNode);

            if (canRegisterUndo) processEditor.undoSystem.AddCommand(new CreateNodeCommand(stepNode, this));
            if (canCallOnReady) stepNode.OnReady();
            if (stepNode.muted) stepNode.OnMute();
            return stepNode;
        }
        public StepNode AddStepAndCreateNode(IStep step)
        {
            currentProcess.AddStep(step);
            return CreateStepNode(step);
        }
        public StepNode CreateStepAndNode(Type stepType, Vector2 position)
        {
            return AddStepAndCreateNode(CreateStep(stepType, position));
        }

        public IEnumerable<Port> GetAllPorts()
        {
            foreach (var stepNode in stepNodeList)
            {
                if (stepNode is IInputable inputable)
                {
                    foreach (var inputPort in inputable.GetInputPorts())
                    {
                        yield return inputPort;
                    }
                }
                if (stepNode is IOutputable outputable)
                {
                    foreach (var outputPort in outputable.GetOutputPorts())
                    {
                        yield return outputPort;
                    }
                }
            }
        }
        public virtual List<Port> GetAvailablePorts(Port startPort)
        {
            List<Port> result = new List<Port>();
            StepNode startNode = startPort.node;
            if (startPort is InputPort)
            {
                foreach (var port in GetAllPorts())
                {
                    if (port is InputPort) continue;
                    if (port.node == startNode) continue;
                    result.Add(port);
                }
            }
            else
            {
                foreach (var port in GetAllPorts())
                {
                    if (port is OutputPort) continue;
                    if (port.node == startNode) continue;
                    result.Add(port);
                }
            }
            return result;
        }
        public Connectionline ConnectPort(InputPort inputPort, OutputPort outputPort)
        {
            Connectionline connectionline = new Connectionline();
            connectionline.normalColor = processEditor.editorData.connectionLineSetting.normalColor;
            connectionline.highlightColor = processEditor.editorData.connectionLineSetting.highlightColor;
            connectionline.stepGraphView = this;
            connectionline.Connect(inputPort, outputPort);
            AddConnectionline(connectionline);
            if (canRegisterUndo) processEditor.undoSystem.AddCommand(new ConnectNodeCommand(connectionline, this));
            return connectionline;
        }

        internal void AddNode(StepNode stepNode)
        {
            stepNodeList.Add(stepNode);
            nodeContainer.Add(stepNode);
            graphElementList.Add(stepNode);
            currentProcess.AddStep(stepNode.step);
            stepNode.BringToFront();
        }
        internal void RemoveNode(StepNode stepNode)
        {
            stepNodeList.Remove(stepNode);
            nodeContainer.Remove(stepNode);
            graphElementList.Remove(stepNode);
            currentProcess.RemoveStep(stepNode.step);
        }

        internal void LoadProcess(EditorProcessInfo editorProcessInfo)
        {
            if (editorProcessInfo == null) return;
            currentEditorProcessInfo = editorProcessInfo;
            stepNodeSearchWindow.currentProcessType = editorProcessInfo.processType;
            LoadCurrentProcess();
        }
        internal void OpenSearchWindow(Vector2 positionInEditorWindow)
        {
            Vector2 editorWindowPosition = processEditor.position.position;
            Vector2 screenMousePosition = editorWindowPosition + positionInEditorWindow;
            SearchWindow.Open(new SearchWindowContext(screenMousePosition), stepNodeSearchWindow);
        }
        internal void SetContentViewPosition(Vector2 position)
        {
            viewTransform.position = new Vector3(position.x, position.y, 0f);
        }

        private void CreateAllConnection(IEnumerable<StepNode> stepNodes, List<Connectionline> connectionlineList = null)
        {
            foreach (var stepNode in stepNodes)
            {
                if (stepNode is not IOutputable outputable) continue;
                foreach (var outputPort in outputable.GetOutputPorts())
                {
                    foreach (var guid in outputPort.connectedNodeGUIDList)
                    {
                        if (!stepNodeDictionary.TryGetValue(guid, out StepNode targetNode))
                        {
                            Debug.LogError($"无法找到目标节点={guid}");
                            continue;
                        }
                        Connectionline connectionline = targetNode.RequestConnect(outputPort);
                        connectionlineList?.Add(connectionline);
                    }
                }
            }
        }
        private void PasteNodes()
        {
            if (copyBufferList.Count == 0) return;
            stepNodeCreateState = StepNodeCreateState.Paste;
            ClearSelection();
            List<StepNode> newStepNodeList = new List<StepNode>(copyBufferList.Count);
            Dictionary<string, string> guidMap = new Dictionary<string, string>();
            foreach (var stepData in copyBufferList)
            {
                IStep step = EditorProcessAssetManager.StringToStep(stepData);
                string oldGUID = step.guid;
                step.guid = EditorProcessAssetManager.NewGUID();
                StepNode stepNode = AddStepAndCreateNode(step);
                stepNode.position += new Vector2(12f, 12f);
                AddToSelection(stepNode);
                guidMap.Add(oldGUID, stepNode.guid);
                newStepNodeList.Add(stepNode);
            }
            List<string> newNodeGUIDList = new List<string>();
            foreach (var stepNode in newStepNodeList)
            {
                if (stepNode is not IOutputable outputable) continue;
                foreach (var outputPort in outputable.GetOutputPorts())
                {
                    newNodeGUIDList.Clear();
                    IList<string> connectedNodeGUIDList = outputPort.connectedNodeGUIDList;
                    foreach (var connectedNodeGUID in connectedNodeGUIDList)
                    {
                        if (!guidMap.TryGetValue(connectedNodeGUID, out string newGUID)) continue;
                        newNodeGUIDList.Add(newGUID);
                    }
                    connectedNodeGUIDList.Clear();
                    foreach (var newNodeGUID in newNodeGUIDList) connectedNodeGUIDList.Add(newNodeGUID);
                }
            }
            List<Connectionline> connectionlineList = new List<Connectionline>();
            List<ConnectionInfo> connectionInfoList = new List<ConnectionInfo>();
            CreateAllConnection(newStepNodeList, connectionlineList);
            foreach (var connectionline in connectionlineList) connectionInfoList.Add(new ConnectionInfo(connectionline));
            processEditor.undoSystem.AddCommand(new PasteNodeCommand(newStepNodeList, connectionInfoList, this));
            stepNodeCreateState = StepNodeCreateState.None;
        }
        private void SelectAllNode()
        {
            foreach (var stepNode in stepNodeList)
            {
                if (stepNode.selected) continue;
                AddToSelection(stepNode);
            }
        }
        private void LoadCurrentProcess()
        {
            stepDictionary.Clear();
            switch (currentProcess.editorData.viewDataSaveTargetType)
            {
                case EditorData.DataSaveTargetType.SaveToLocal:
                    ViewData viewData = fileViewData.Load(currentEditorProcessInfo);
                    SetContentViewPosition(viewData.offset);
                    zoomer.SetZoom(viewData.scale);
                    break;
                case EditorData.DataSaveTargetType.SaveToFile:
                    SetContentViewPosition(currentProcess.editorData.viewData.offset);
                    zoomer.SetZoom(currentProcess.editorData.viewData.scale);
                    break;
            }

            stepNodeCreateState = StepNodeCreateState.CreatingFromFile;
            CreateStepNode(currentProcess.startStep);
            foreach (var step in currentProcess) CreateStepNode(step);
            CreateAllConnection(stepNodeList);
            stepNodeCreateState = StepNodeCreateState.None;
            foreach (var stepNode in stepNodeList) stepNode.OnReady();
            if (!string.IsNullOrEmpty(titleFilter)) NodeFilter(titleFilter);
        }
        /// <summary>
        /// 处理扩展节点
        /// </summary>
        private void ReflectCustomStepNode()
        {
            Type startStepType = typeof(StartStep);
            Type editorBaseType = typeof(StepNode);
            Assembly selfAssembly = Assembly.GetExecutingAssembly();
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly == selfAssembly) continue;
                foreach (var type in assembly.GetTypes())
                {
                    if (!type.IsSubclassOf(editorBaseType)) continue;
                    Type genericType = type;
                    while (!genericType.IsGenericType) genericType = genericType.BaseType;
                    Type stepType = genericType.GetGenericArguments()[0];
                    if (stepNodeTypeDictionary.ContainsKey(stepType))
                    {
                        Debug.LogError($"流程编辑器: 类型 {stepType.Name} 被多个编辑器步骤节点指定, 请检查重复");
                        continue;
                    }
                    stepNodeTypeDictionary.Add(stepType, type);

                    if (stepType.IsSubclassOf(startStepType)) continue;//是开发者自定义的StartStep的子类, 直接跳过

                    StepNodeSearchTreeGroupAttribute stepNodeSearchTreeGroupAttribute = type.GetCustomAttribute<StepNodeSearchTreeGroupAttribute>();
                    if (stepNodeSearchTreeGroupAttribute == null)
                    {
                        Debug.LogError($"流程编辑器: 类型 {type.Name} 没有附加 {nameof(StepNodeSearchTreeGroupAttribute)} 特性, 无法在编辑器里创建");
                        continue;
                    }

                    //自定义Process特性
                    IEnumerable<CustomProcessAttribute> customProcessAttributes = type.GetCustomAttributes<CustomProcessAttribute>();
                    if (customProcessAttributes.Count() == 0)
                    {
                        stepNodeSearchWindow.AddCommonEntry(stepNodeSearchTreeGroupAttribute.groupName, stepType);
                        continue;
                    }
                    foreach (var customProcessAttribute in customProcessAttributes)
                    {
                        Type processSubclassType = customProcessAttribute.processSubclassType;
                        if (processSubclassType.GetInterface("winS.Unity.Process.IProcess") == null)
                        {
                            Debug.LogError($"剧情编辑器警告: {nameof(CustomProcessAttribute)} 指定的类型 {processSubclassType.Name} 并不是一个有效的流程类型, 因为它不继承自 {nameof(IProcess)}");
                            continue;
                        }
                        stepNodeSearchWindow.AddPrivateEntry(processSubclassType, stepNodeSearchTreeGroupAttribute.groupName, stepType);
                    }
                }
            }
        }
        private void CutNodes(IEnumerable<StepNode> stepNodes)
        {
            if (stepNodes.Count() == 0) return;
            stepNodeCreateState = StepNodeCreateState.Cut;
            CopyNodes(stepNodes);
            DeleteElementsCore(stepNodes);
            stepNodeCreateState = StepNodeCreateState.None;
        }
        private void MuteElements(IEnumerable<GraphElement> graphElements)
        {
            foreach (var graphElement in graphElements) graphElement.Mute();
            processEditor.undoSystem.AddCommand(new MuteCommand(graphElements));
        }
        private void UnmuteElements(IEnumerable<GraphElement> graphElements)
        {
            foreach (var graphElement in graphElements) graphElement.Unmute();
            processEditor.undoSystem.AddCommand(new UnmuteCommand(graphElements));
        }
        private void CopyNodes(IEnumerable<StepNode> stepNodes)
        {
            copyBufferList.Clear();
            foreach (var stepNode in stepNodes)
            {
                if (!stepNode.copyable) continue;
                copyBufferList.Add(EditorProcessAssetManager.StepToString(stepNode.step));
            }
        }
        private void DuplicateNodes(IList<StepNode> stepNodes)
        {
            if (stepNodes.Count == 0) return;
            CopyNodes(stepNodes);
            PasteNodes();
        }
        private void DisconnectAllNodes(IEnumerable<StepNode> stepNodes)
        {
            List<DisconnectNodeCommand> disconnectNodeCommandList = new List<DisconnectNodeCommand>();
            foreach (var stepNode in stepNodes)
            {
                if (stepNode is IInputable inputable)
                {
                    foreach (var inputPort in inputable.GetInputPorts())
                    {
                        Disconnect(inputPort.connectionlines.ToList());
                    }
                }
                if (stepNode is IOutputable outputable)
                {
                    foreach (var outputPort in outputable.GetOutputPorts())
                    {
                        Disconnect(outputPort.connectionlines.ToList());
                    }
                }
            }
            if (disconnectNodeCommandList.Count == 0) return;
            processEditor.undoSystem.AddCommand(new GroupCommand("断开节点所有连接", disconnectNodeCommandList));

            void Disconnect(IEnumerable<Connectionline> connectionlines)
            {
                foreach (var connectionline in connectionlines)
                {
                    connectionline.Disconnect();
                    RemoveConnectionline(connectionline);
                    disconnectNodeCommandList.Add(new DisconnectNodeCommand(connectionline, this));
                }
            }
        }
        private void FocusElement(GraphElement graphElement)
        {
            Vector2 position = graphElement.position + graphElement.size * 0.5f;
            float scale = viewTransform.scale.x;
            Vector2 centerOffset = new Vector2(layout.width * 0.5f, layout.height * 0.5f);
            viewTransform.position = centerOffset - position * scale;
        }
        private void DeleteElements(IEnumerable<GraphElement> graphElements)
        {
            stepNodeCreateState = StepNodeCreateState.Deleting;
            DeleteElementsCore(graphElements);
            stepNodeCreateState = StepNodeCreateState.None;
        }
        private void DeleteElementsCore(IEnumerable<GraphElement> graphElements)
        {
            List<StepNode> nodeDeleteList = new List<StepNode>();
            Dictionary<Connectionline, ConnectionInfo> connectionInfoDictionary = new Dictionary<Connectionline, ConnectionInfo>();
            foreach (var graphElement in graphElements)
            {
                if (!graphElement.deletable) continue;
                if (graphElement is not StepNode stepNode) continue;
                if (stepNode is IInputable inputable)
                {
                    foreach (var inputPortConnectionline in inputable.GetInputPorts().SelectMany(inputPort => inputPort.connectionlines))
                    {
                        if (connectionInfoDictionary.ContainsKey(inputPortConnectionline)) continue;
                        connectionInfoDictionary.Add(inputPortConnectionline, new ConnectionInfo(inputPortConnectionline));
                    }
                }
                if (stepNode is IOutputable outputable)
                {
                    foreach (var outputPortConnectionline in outputable.GetOutputPorts().SelectMany(outputPort => outputPort.connectionlines))
                    {
                        if (connectionInfoDictionary.ContainsKey(outputPortConnectionline)) continue;
                        connectionInfoDictionary.Add(outputPortConnectionline, new ConnectionInfo(outputPortConnectionline));
                    }
                }
                nodeDeleteList.Add(stepNode);
                RemoveNode(stepNode);
            }
            Dictionary<Connectionline, ConnectionInfo>.ValueCollection connectionInfoCollection = connectionInfoDictionary.Values;
            if (canRegisterDeleteUndo) processEditor.undoSystem.AddCommand(stepNodeCreateState == StepNodeCreateState.Cut ? new CutNodeCommand(nodeDeleteList, connectionInfoCollection, this) : new DeleteElementCommand(nodeDeleteList, connectionInfoCollection, this));

            foreach (var connectionInfo in connectionInfoCollection)
            {
                connectionInfo.connectionline.Disconnect();
                RemoveConnectionline(connectionInfo.connectionline);
            }
            ClearSelection();
        }
        private void RegisterEditorSettingChanged(ProcessEditorData processEditorData)
        {
            processEditorData.connectionLineSetting.normalColor.onValueChanged += newValue =>
            {
                foreach (var connectionline in connectionlineList) connectionline.normalColor = newValue;
            };
            processEditorData.connectionLineSetting.highlightColor.onValueChanged += newValue =>
            {
                foreach (var connectionline in connectionlineList) connectionline.highlightColor = newValue;
                foreach (var port in GetAllPorts()) port.connector.highlightColor = newValue;
            };
        }

        private void KeyDownCallback(KeyDownEvent keyDownEvent)
        {
            switch (keyDownEvent.keyCode)
            {
                case KeyCode.M:
                    keyDownEvent.StopPropagation();
                    if (keyDownEvent.ctrlKey && selectionHashSet.Count > 0)
                    {
                        IEnumerable<GraphElement> muteableElements = selectionHashSet.Where(selection => selection.muteable);
                        bool allMute = muteableElements.All(element => element.muted);
                        bool anyMute = allMute || muteableElements.Any(element => element.muted);
                        if (allMute) UnmuteElements(muteableElements.ToList());
                        else if (anyMute) UnmuteElements(muteableElements.Where(element => element.muted).ToList());
                        else MuteElements(muteableElements.ToList());
                    }
                    return;
                case KeyCode.F:
                    keyDownEvent.StopPropagation();
                    if (selectionHashSet.Count > 0) FocusElement(selectionHashSet.First());
                    return;
                default: return;
            }
        }
        private void MouseDownCallback(MouseDownEvent mouseDownEvent)
        {
            if (mouseDownEvent.button != 1) return;
            VisualElement targetElement = mouseDownEvent.target as VisualElement;
            mouseDownEvent.StopPropagation();
            if (targetElement != this)
            {
                GraphElement rootElement = targetElement.GetFirstOfType<GraphElement>();
                panel.contextualMenuManager.DisplayMenu(mouseDownEvent, rootElement);
                return;
            }
            panel.contextualMenuManager.DisplayMenu(mouseDownEvent, this);
        }
        private void ValidateCommandCallback(ValidateCommandEvent validateCommandEvent)
        {
            switch (validateCommandEvent.commandName)
            {
                case "SoftDelete" or "Delete": if (selectionHashSet.Count > 0) DeleteElements(selectionHashSet); return;
                case "Paste": PasteNodes(); return;
                case "SelectAll": SelectAllNode(); return;
                case "Cut": CutNodes(selectionHashSet.OfType<StepNode>().ToList()); return;
                case "Copy": CopyNodes(selectionHashSet.OfType<StepNode>().ToList()); return;
                case "Duplicate": DuplicateNodes(selectionHashSet.OfType<StepNode>().ToList()); return;
                default: return;
            }
        }
        private void OnCreateNodeMenuContext(DropdownMenuAction dropdownMenuAction)
        {
            waitConnectionOutputPort = null;
            OpenSearchWindow(dropdownMenuAction.eventInfo.mousePosition);
        }

        /// <summary>
        /// 上下文菜单回调，这个是Visual自定义上下文Callback
        /// AppendAction会自动唤和设置上下文
        /// </summary>
        /// <param name="contextualMenuPopulateEvent"></param>
        private void ContextualMenuPopulateCallback(ContextualMenuPopulateEvent contextualMenuPopulateEvent)
        {
            contextualMenuPopulateEvent.menu.MenuItems().Clear();
            IEventHandler target = contextualMenuPopulateEvent.target;
            if (target == this)
            {
                contextualMenuPopulateEvent.menu.AppendAction("全选 %A", _ => SelectAllNode(), currentProcess == null ? DropdownMenuAction.Status.Disabled : DropdownMenuAction.Status.Normal);
                contextualMenuPopulateEvent.menu.AppendAction("粘贴 %V", dropdownMenuAction => PasteNodes(), dropdownMenuAction => copyBufferList.Count != 0 ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
                contextualMenuPopulateEvent.menu.AppendSeparator();
                contextualMenuPopulateEvent.menu.AppendAction("创建节点", OnCreateNodeMenuContext, currentProcess == null ? DropdownMenuAction.Status.Disabled : DropdownMenuAction.Status.Normal);
                return;
            }
            if (target is Connectionline connectionline)
            {
                contextualMenuPopulateEvent.menu.AppendAction("断开", dropdownMenuAction =>
                {
                    connectionline.Disconnect();
                    RemoveConnectionline(connectionline);
                    processEditor.undoSystem.AddCommand(new DisconnectNodeCommand(connectionline, this));
                }, DropdownMenuAction.Status.Normal);
                return;
            }
            if (target is StepNode node)
            {
                List<StepNode> selectionList = selectionHashSet.OfType<StepNode>().ToList();
                bool allMute = selectionList.All(stepNode => stepNode.muted);
                bool anyMute = allMute || selectionList.Any(stepNode => stepNode.muted);
                if (allMute) contextualMenuPopulateEvent.menu.AppendAction("取消静音 %M", dropdownMenuAction => UnmuteElements(dropdownMenuAction.userData as IEnumerable<StepNode>), dropdownMenuAction => node.muteable ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, selectionList);
                else if (anyMute)
                {
                    contextualMenuPopulateEvent.menu.AppendAction("静音 %M", dropdownMenuAction => MuteElements(dropdownMenuAction.userData as IEnumerable<StepNode>), dropdownMenuAction => (allMute || !node.muteable) ? DropdownMenuAction.Status.Disabled : DropdownMenuAction.Status.Normal, selectionList.Where(node => node.muteable && !node.muted).ToList());
                    contextualMenuPopulateEvent.menu.AppendAction("取消静音 %M", dropdownMenuAction => UnmuteElements(dropdownMenuAction.userData as IEnumerable<StepNode>), dropdownMenuAction => node.muteable ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, selectionList.Where(node => node.muteable && node.muted).ToList());
                }
                else contextualMenuPopulateEvent.menu.AppendAction("静音 %M", dropdownMenuAction => MuteElements(dropdownMenuAction.userData as IEnumerable<StepNode>), dropdownMenuAction => (allMute || !node.muteable) ? DropdownMenuAction.Status.Disabled : DropdownMenuAction.Status.Normal, selectionList.Where(node => node.muteable && !node.muted).ToList());
                List<StepNode> hasConnectionNodeList = selectionList.Where(stepNode => stepNode.hasAnyConnection).ToList();
                contextualMenuPopulateEvent.menu.AppendAction("断开所有连接", dropdownMenuAction => DisconnectAllNodes(dropdownMenuAction.userData as IEnumerable<StepNode>), dropdownMenuAction => hasConnectionNodeList.Count > 0 ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, hasConnectionNodeList);

                contextualMenuPopulateEvent.menu.AppendSeparator();
                contextualMenuPopulateEvent.menu.AppendAction("聚焦", dropdownMenuAction => FocusElement(node), DropdownMenuAction.Status.Normal);
                contextualMenuPopulateEvent.menu.AppendAction("复制 %C", dropdownMenuAction => CopyNodes(dropdownMenuAction.userData as List<StepNode>), dropdownMenuAction => node.copyable ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, selectionList);
                contextualMenuPopulateEvent.menu.AppendAction("剪切 %X", dropdownMenuAction => CutNodes(dropdownMenuAction.userData as List<StepNode>), dropdownMenuAction => node.copyable ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, selectionList);
                contextualMenuPopulateEvent.menu.AppendAction("粘贴 %V", dropdownMenuAction => PasteNodes(), dropdownMenuAction => copyBufferList.Count != 0 ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
                contextualMenuPopulateEvent.menu.AppendAction("复制并粘贴 %D", dropdownMenuAction => DuplicateNodes(dropdownMenuAction.userData as List<StepNode>), dropdownMenuAction => node.copyable ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, selectionList);
                contextualMenuPopulateEvent.menu.AppendSeparator();
                contextualMenuPopulateEvent.menu.AppendAction("删除", dropdownMenuAction => DeleteElements(dropdownMenuAction.userData as List<StepNode>), dropdownMenuAction => node.deletable ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled, selectionList);
                return;
            }
        }
    }
}